using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Collections;
namespace InlineASM
{
    public class InlineASM
    {
        [DllImport("user32", EntryPoint = "CallWindowProc")]
        public static extern int CallWindowProcA(int lpPrevWndFunc, int hwnd, int MSG, int wParam, int lParam);

        private UnmanagedMemoryStream ms;
        private Hashtable procedures = new Hashtable();
        public readonly int memAddress;

        //Constructor(s)
        public InlineASM(string hexBytes) : this(StringToByteArray(hexBytes)) { }
        public InlineASM(byte[] asmData)
        {
            UnmanagedBuffer pb = new UnmanagedBuffer(asmData);
            memAddress = pb.baseAddress.ToInt32();
            ms = (UnmanagedMemoryStream)pb.GetStream();
        }

        //Public Routines
        public void addProc(String routineName, int startOffset)
        {
            procedures.Add(routineName, startOffset);
        }
        public int runProc(String routineName, Object param1, Object param2, Object param3,Object param4)
        {
            return 0;
            /*int offset = -1;
            
            foreach (DictionaryEntry de in procedures)
            {
                if (de.Key.Equals(routineName))
                {
                    offset = (int)de.Value;
                }
            }

            if (offset == -1) return -1;
            
            return CallWindowProcA(memAddress + offset, VarPtr(param1), VarPtr(param2), VarPtr(param3), VarPtr(param4));*/

        }
        public void removeProc(String routineName)
        {
            if (procedures.ContainsKey(routineName))
            {
                procedures.Remove(routineName);
            }
        }
        public static int VarPtr(object o)
        {
            if (o == null) return 0;
            System.Runtime.InteropServices.GCHandle GC =
            System.Runtime.InteropServices.GCHandle.Alloc(o,
            System.Runtime.InteropServices.GCHandleType.Pinned);
            int ret = GC.AddrOfPinnedObject().ToInt32();

            return ret;
        }
        //Private Routines
        
        private static byte[] StringToByteArray(String hex)
        {
            int NumberChars = hex.Length;
            byte[] bytes = new byte[NumberChars / 2];
            for (int i = 0; i < NumberChars; i += 2)
                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
            return bytes;
        }
    }

    public class UnmanagedBuffer : IDisposable
    {
        private const int MEM_COMMIT = 0x1000;
        private const int MEM_DECOMMIT = 0x4000;
        private const int MEM_RELEASE = 0x8000;
        private const int PAGE_NOACCESS = 0x001;
        private const int PAGE_READONLY = 0x002;
        private const int PAGE_READWRITE = 0x004;
        private const int PAGE_WRITECOPY = 0x008;
        private const int PAGE_EXECUTE = 0x010;
        private const int PAGE_EXECUTE_READ = 0x020;
        private const int PAGE_EXECUTE_READWRITE = 0x040;
        private const int PAGE_EXECUTE_WRITECOPY = 0x080;
        private const int PAGE_GUARD = 0x100;
        private const int PAGE_NOCACHE = 0x200;

        [DllImport("kernel32", SetLastError = true)]
        private static extern IntPtr VirtualAlloc(
            IntPtr lpStartAddr,
            int size,
            uint flAllocationType,
            uint flProtect);

        [DllImport("kernel32", SetLastError = true)]
        private static extern int VirtualFree(
            IntPtr lpAddress,
            UInt32 dwSize,
            UInt32 dwFreeType);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern int VirtualProtect(
            IntPtr lpAddress,
            int dwSize,
            int flNewProtect,
            ref int lpflOldProtect);

        public IntPtr baseAddress;

        private bool disposed;

        private int length;

        private UnmanagedBuffer() { }


        // Initializes a buffer of the desired length.  No error
        // handling here...
        public UnmanagedBuffer(byte[] data)
        {
            if (data.Length < 1)
                throw new ArgumentOutOfRangeException("length");

            this.length = data.Length;

            // Allocate the desired memory.  The first parameter
            // specifies the address; if null/zero, the system
            // decides where to locate the memory.

            baseAddress = VirtualAlloc(
                IntPtr.Zero,
                length,
                MEM_COMMIT,
                PAGE_EXECUTE_READWRITE);
            Stream s = GetStream();
            foreach (byte b in data)
            {
                s.WriteByte(b);
            }

        }
        public UnmanagedBuffer(String hexString)
            : this(StringToByteArray(hexString))
        { }
        private static byte[] StringToByteArray(String hex)
        {
            int NumberChars = hex.Length;
            byte[] bytes = new byte[NumberChars / 2];
            for (int i = 0; i < NumberChars; i += 2)
                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
            return bytes;
        }
        public UnmanagedBuffer(int length)
        {
            if (length < 1)
                throw new ArgumentOutOfRangeException("length");
            this.length = length;
            baseAddress = VirtualAlloc(
                IntPtr.Zero,
                length,
                MEM_COMMIT,
                PAGE_EXECUTE_READWRITE);
        }

        ~UnmanagedBuffer()
        {
            Dispose(false);
        }


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }



        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                try
                {
                    int result = VirtualFree(
                        baseAddress,
                        0,
                        MEM_DECOMMIT | MEM_RELEASE);


                }
                catch
                { }
                this.disposed = true;
            }
        }


        // The GetStream method returns a stream capable of
        // reading and writing to the allocated buffer.

        public Stream GetStream()
        {
            unsafe
            {

                if (this.disposed)
                    throw new ObjectDisposedException("this");

                byte* memoryPointer = (byte*)baseAddress.ToPointer();

                UnmanagedMemoryStream stream = new UnmanagedMemoryStream(
                    memoryPointer,
                    length,
                    length,
                    FileAccess.ReadWrite);
                return stream;
            }
        }
    }
}
